#include "stdafx.h"
#include "macroSlider.h"
#include "qalgorithms.h"

namespace{
    const int WIDTH = 128;
	const int HEIGHT = 35;
	const int SLIDE_ZONE_HEIGHT = 10;
	const int BORDER_W = 40;
	const int BORDER_H = 2;
	const int RADIUS = 5;
	const int SIDE = 6;
	const QColor selectedZoneColorUnselected(99, 135, 184, 200);
	const QColor selectedZoneColorSelected(129, 165, 18, 200);
	const QColor sliderColorUnselected(99, 135, 184);
	const QColor sliderColorSelected(129, 165, 18);
	const QColor backgroundColor(208, 217, 216);
	const QSize labelSize(40, 16);
	const QSize labelCurrValue(30, 12);
}

macroSlider::macroSlider(QWidget* parent)
    : QWidget(parent)
    , Min(0)
    , Max(100)
	, Step(1)
	, subSplit(0)
	, value1(0)
    , value2(0)
	, digits(0)
    , IsMouseOverSlider1(false)
	, IsMouseOverSlider2(false)
	, IsMouseOverSelectedZone(false)
    , IsMovingSlider1(false)
	, IsMovingSlider2(false)
	, IsMovingSelectedZone(false)
{
    this->setMouseTracking(true);
    this->setFocusPolicy(Qt::ClickFocus);
    this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
}

void macroSlider::setValue1(double value){
	value = qMin(qMax(Min, value), Max);
	value = qMin(value, value2);
	round(value);
    if (value != value1) {
        value1 = value;
        this->repaint();
		emit Value1Changed(value1);
		emit ZoneChanged(value1, value2);
	}
}

void macroSlider::setValue2(double value){
    value = qMin(qMax(Min, value), Max);
	value = qMax(value, value1);
	round(value);
    if (value != value2) {
        value2 = value ;
        this->repaint();
        emit Value1Changed(value2);
		emit ZoneChanged(value1, value2);
	}
}

void macroSlider::setValues(double val1, double val2){
	bool isChanged = false;
	value2 = Max;
	val1 = qMin(qMax(Min, val1), Max);
	val1 = qMin(val1, value2);
	round(val1);
    if (val1 != value1) {
        value1 = val1;
		isChanged = true;
		emit Value1Changed(value1);
	}
	val2 = qMin(qMax(Min, val2), Max);
	val2 = qMax(val2, value1);
	round(val2);
    if (val2 != value2) {
        value2 = val2;
        isChanged = true;
        emit Value1Changed(value2);
	}
	if (isChanged == true){
		emit ZoneChanged(value1, value2);
		this->repaint();
	}
}

void macroSlider::setRange(double min, double max){
    Min = min;
    Max = max;
    this->repaint();
}

void macroSlider::increaseMaxValue(double max){
	/*if (value2 != Max){
		Max = max;
		this->repaint();
	}else{*/
		Max = max;
		setValues(value2 != value1 ? Max - (value2 - value1) : Min, Max);
	//}
}

void macroSlider::setStep(double step){
	round(step);
	if (step > 0 && step <= Max - Min){
		Step = step;
		this->repaint();
	}
}

void macroSlider::setSubSplit(int count){
	if (count >= 1){
		subSplit = count;
		this->repaint();
	}
}

void macroSlider::setDigits(int count){
	if (count >= 0){
		digits = count;
		this->repaint();
	}
}

QSize macroSlider::minimumSizeHint() const{
    return QSize(WIDTH, HEIGHT);
}

void macroSlider::leaveEvent(QEvent* /*event*/){
	if (IsMouseOverSlider1 || IsMouseOverSlider2 || IsMouseOverSelectedZone){
		IsMouseOverSlider1 = false;
		IsMouseOverSlider2 = false;
		IsMouseOverSelectedZone = false;
		 this->repaint();
	}
}

void macroSlider::mouseMoveEvent(QMouseEvent* event){
    if (!(IsMovingSlider1 || IsMovingSlider2 || IsMovingSelectedZone)) checkMouseOver(event->pos());

	if (IsMovingSlider1) {
		setValue1(getValueFromPos(event->x() - distance));
    }else if(IsMovingSlider2){
		setValue2(getValueFromPos(event->x() - distance));
	}else if(IsMovingSelectedZone){
		const double value = getValueFromPos(event->x());
		const double new_val1 = value - dist_to_value1;
		const double new_val2 = value + dist_to_value2;
		if (new_val1 > Min && new_val2 < Max) setValues(new_val1, new_val2);
	}
}

void macroSlider::checkMouseOver(const QPoint &current_pos){
	    if (!IsMouseOverSlider1 && Slider1Rect.contains(current_pos)) {
        IsMouseOverSlider1 = true;
        this->repaint();
    } else if (IsMouseOverSlider1 && !Slider1Rect.contains(current_pos)) {
        IsMouseOverSlider1 = false;
        this->repaint();
    }

	if (!IsMouseOverSlider2 && Slider2Rect.contains(current_pos)) {
        IsMouseOverSlider2 = true;
        this->repaint();
    } else if (IsMouseOverSlider2 && !Slider2Rect.contains(current_pos)) {
        IsMouseOverSlider2 = false;
        this->repaint();
    }

	if (!IsMouseOverSelectedZone && SelectedZoneRect.contains(current_pos)) {
        IsMouseOverSelectedZone = true;
        this->repaint();
    } else if (IsMouseOverSelectedZone && !SelectedZoneRect.contains(current_pos)) {
        IsMouseOverSelectedZone = false;
        this->repaint();
    }
}

void macroSlider::mousePressEvent(QMouseEvent* event){
    if (Slider1Rect.contains(event->pos())){
		IsMovingSlider1 = true;
		distance = event->x() - getPosFromValue(value1);
    }else if (Slider2Rect.contains(event->pos())){
		IsMovingSlider2 = true;
		distance = event->x() - getPosFromValue(value2);
	}else if (SelectedZoneRect.contains(event->pos())){
		IsMovingSelectedZone = true;
		dist_to_value1 = getValueFromPos(event->x()) - value1;
		dist_to_value2 = value2 - getValueFromPos(event->x());
	}
}

void macroSlider::mouseReleaseEvent(QMouseEvent* /*event*/){
    IsMovingSlider1 = false;
	IsMovingSlider2 = false;
	IsMovingSelectedZone = false;
}

void macroSlider::keyPressEvent(QKeyEvent* event){
    const double tick = pow(10, -digits);
    if (event->key() == Qt::Key_Left){
		if (value1 - tick >= Min){
			setValues(value1 - tick, value2 - tick);
		}
    }else if (event->key() == Qt::Key_Right){
        if (value2 + tick <= Max){
			setValues(value1 + tick, value2 + tick);
		}
    }
}

void macroSlider::paintEvent(QPaintEvent* event){
    WIDTH = event->rect().width();
    QPainter painter(this);
    const QBrush shadow = palette().shadow();
    const QBrush line = Qt::black;
    const QBrush light = palette().light();
	QFont font;
	font.setPointSize(8);
	painter.setFont(font);	
    painter.setPen(shadow.color());
    painter.setBrush(backgroundColor);
	painter.drawRect(BORDER_W, BORDER_H, WIDTH - 2 * BORDER_W, SLIDE_ZONE_HEIGHT);

	painter.setBrush(shadow);
    painter.setPen(line.color());	
	//scale
	painter.setBrush(shadow.color());
	double x_step = (WIDTH - 2 * BORDER_W) / (Max - Min) * Step;
	double x_pos = BORDER_W;
	for(double label_value = Min; label_value < Max + Step; label_value += Step){
		double value = qMin(label_value, Max);
		QRectF labelRect(x_pos - labelSize.width() / 2, BORDER_H + SLIDE_ZONE_HEIGHT, labelSize.width(), labelSize.height());
		painter.drawText(labelRect, Qt::AlignCenter,  QString::number(value, 'f', digits));
		painter.drawLine(QPointF(x_pos, BORDER_H + 2), QPointF(x_pos, BORDER_H + SLIDE_ZONE_HEIGHT - 2));
		if (subSplit > 1){
			double sub_x_step = x_step / subSplit;   
			for (int i = 1; i < subSplit; ++i){
				if (x_pos + i * sub_x_step >= WIDTH - BORDER_W) break;
				painter.drawEllipse(QPoint(x_pos + i * sub_x_step, BORDER_H + SLIDE_ZONE_HEIGHT / 2), 1, 1);
			}
		}
		x_pos += x_step;
		x_pos = qMin(x_pos, WIDTH - BORDER_W);
	}
	
	//draw selected zone	
	const double x[] = {getPosFromValue(value1), getPosFromValue(value2)};
	const double v[] = {value1, value2};
	const QColor c[] = {IsMouseOverSlider1 ? sliderColorSelected : sliderColorUnselected,
						IsMouseOverSlider2 ? sliderColorSelected : sliderColorUnselected};	
	painter.setBrush(IsMouseOverSelectedZone ? selectedZoneColorSelected : selectedZoneColorUnselected);
	painter.drawRect(x[0], BORDER_H, x[1] - x[0], SLIDE_ZONE_HEIGHT);
	
	// draw sliders
	font.setPointSize(7);
	font.setBold(true);
	painter.setFont(font);	
	for(int i = 0; i <= 1; ++i){
		painter.setBrush(c[i]);
		int coeff = (2 * i - 1);
		QRectF labelRect(x[i] + (i - 1) * labelCurrValue.width() + coeff * SIDE, 
			BORDER_H + SLIDE_ZONE_HEIGHT / 2 -  labelCurrValue.height() / 2, labelCurrValue.width(), labelCurrValue.height());
		painter.drawRect(labelRect);
		painter.setPen(light.color());
		painter.drawText(labelRect, Qt::AlignCenter,  QString::number(v[i], 'f', digits));
		painter.setPen(line.color());
		const QPoint slider[] = {
			QPoint(x[i], 1),
			QPoint(x[i], SLIDE_ZONE_HEIGHT + 1.5 * SIDE),
			QPoint(x[i] + coeff * SIDE, SLIDE_ZONE_HEIGHT + SIDE / 2),
			QPoint(x[i] + coeff * SIDE, 1),
			QPoint(x[i], 0),
		};
		painter.drawPolygon(slider, sizeof(slider) / sizeof(slider[0]));
	}

	
	//rect
	Slider1Rect = QRect(x[0] - SIDE - labelCurrValue.width(), BORDER_H, SIDE + labelCurrValue.width(), SLIDE_ZONE_HEIGHT + 1.5 * SIDE);
	Slider2Rect = QRect(x[1], BORDER_H, SIDE + labelCurrValue.width(), SLIDE_ZONE_HEIGHT + 1.5 * SIDE);
	SelectedZoneRect = QRect(x[0] , BORDER_H, x[1] - x[0], BORDER_H + SLIDE_ZONE_HEIGHT);
}

double macroSlider::getPosFromValue(const double &value) const {
	double pos = BORDER_W + (WIDTH - 2 * BORDER_W) / (Max - Min) * (value - Min);
    if (pos < SIDE) pos = SIDE;
    else if (pos + SIDE > BORDER_W + WIDTH) pos = BORDER_W + WIDTH - SIDE;
	return pos;
}

double macroSlider::getValueFromPos(const double &pos) const{
	double value = (Max - Min) * (pos - BORDER_W) / (WIDTH - 2* BORDER_W) + Min;
	return value;
}

void macroSlider::round(double &value){
	value = QString::number(value, 'f', digits).toDouble();
}